#pragma once
#include <chrono>
#include <imgui/imgui.h>
namespace ciallo {

namespace impl {
template <typename Base, bool need_this = false> class ScopeGuard {
    const bool _ok;

  public:
    constexpr ScopeGuard(bool ok) : _ok(ok) {}
    ~ScopeGuard() {
        if constexpr (need_this) {
            static_cast<Base *>(this)->onExit();
        } else {
            Base::onExit();
        }
    }

    template <typename Func> constexpr bool operator<<(Func func) const {
        if (_ok) {
            func();
        }
        return _ok;
    }
};
} // namespace impl

class WindowScope : public impl::ScopeGuard<WindowScope> {
  public:
    WindowScope(const char *title, ImGuiWindowFlags flags = 0)
        : impl::ScopeGuard<WindowScope>(ImGui::Begin(title, 0, flags)) {}
    static void onExit() { ImGui::End(); }
};

template <typename Time>
class CPUTimeScope : public impl::ScopeGuard<CPUTimeScope<Time>, true> {
  public:
    CPUTimeScope() : impl::ScopeGuard<CPUTimeScope<Time>, true>(true) {
        _start = std::chrono::high_resolution_clock::now();
    }
    void onExit() {
        const auto end = std::chrono::high_resolution_clock::now();
        const auto duration = std::chrono::duration_cast<Time>(end - _start);
        if constexpr (std::is_same_v<Time, std::chrono::nanoseconds>) {
            printf("CPU time: %lld ns\n", duration.count());
        } else if constexpr (std::is_same_v<Time, std::chrono::microseconds>) {
            printf("CPU time: %lld us\n", duration.count());
        } else if constexpr (std::is_same_v<Time, std::chrono::milliseconds>) {
            printf("CPU time: %lld ms\n", duration.count());
        } else if constexpr (std::is_same_v<Time, std::chrono::seconds>) {
            printf("CPU time: %lld s\n", duration.count());
        }
    }

    std::chrono::high_resolution_clock::time_point _start;
};

using CPUTimeScope_s = CPUTimeScope<std::chrono::seconds>;
using CPUTimeScope_ms = CPUTimeScope<std::chrono::milliseconds>;
using CPUTimeScope_us = CPUTimeScope<std::chrono::microseconds>;
using CPUTimeScope_ns = CPUTimeScope<std::chrono::nanoseconds>;

} // namespace ciallo